<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <title>Android应用程序窗口（Activity）的绘图表面（Surface）的创建过程分析 - 老罗的Android之旅
        - 博客频道 - CSDN.NET</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="description" content="在前文中，我们分析了应用程序窗口连接到WindowManagerService服务的过程。在这个过程中，WindowManagerService服务会为应用程序窗口创建过一个到SurfaceFlinger服务的连接。有了这个连接之后，WindowManagerService服务就可以为应用程序窗口创建绘图表面了，以便可以用来渲染窗口的UI。在本文中，我们就详细分析应用程序窗口的绘图表面的创建过程。" />
    <script src="http://static.blog.csdn.net/scripts/jquery.js" type="text/javascript"></script>
    <script type="text/javascript" src="http://static.blog.csdn.net/scripts/ad.js?v=1.1"></script>
        <!--new top-->
               <link rel="stylesheet" href="http://static.csdn.net/public/common/toolbar/css/index.css">        <!--new top-->

    <link rel="Stylesheet" type="text/css" href="http://static.blog.csdn.net/skin/default/css/style.css?v=1.1" />
    <link id="RSSLink" title="RSS" type="application/rss+xml" rel="alternate" href="/Luoshengyang/rss/list" />
    <link rel="shortcut icon" href="/favicon.ico" />
    <link type="text/css" rel="stylesheet" href="http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/default.css" />
 

</head>
<body>
    
   
      <!--new top-->
    <script id="toolbar-tpl-scriptId" fixed="true" prod="blog" skin="black" src="http://static.csdn.net/public/common/toolbar/js/html.js" type="text/javascript"></script>
     <!--new top-->
    <div id="container">
        <div id="header">
    <div class="header">
        <div id="blog_title">
            <h2>
                <a href="http://blog.csdn.net/luoshengyang">老罗的Android之旅</a></h2>
            <h3>爱生活，爱Android</h3>
            <div class="clear">
            </div>
        </div>
        <div class="clear">
        </div>
    </div>
</div>
<div id="navigator">
    <div class="navigator_bg">
    </div>
    <div class="navigator">
        <ul>
                <li id="btnContents"><a href="http://blog.csdn.net/luoshengyang?viewmode=contents"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_mulu'])">
                    <img src="http://static.blog.csdn.net/images/ico_list.gif">目录视图</span></a></li>
                <li id="btnView"><a href="http://blog.csdn.net/luoshengyang?viewmode=list"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_zhaiyao'])">
                    <img src="http://static.blog.csdn.net/images/ico_summary.gif">摘要视图</span></a></li>
                <li id="btnRss"><a href="http://blog.csdn.net/luoshengyang/rss/list"><span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_RSS'])">
                    <img src="http://static.blog.csdn.net/images/ico_rss.gif">订阅</span></a></li>
</ul>
    </div>
</div>
<script type="text/javascript">
    var username = "Luoshengyang";
    var _blogger = username;
    var blog_address = "http://blog.csdn.net/luoshengyang";
    var static_host = "http://static.blog.csdn.net";
    var currentUserName = "";
</script>

        <div id="body">
            <div id="main">
                <div class="main">
<div class="notice tracking-ad" data-mod='popu_3' > 


<a href="http://hero.csdn.net/Question/Details?ID=351&ExamID=346" target="_blank"><font color=blue>英雄会第四届在线编程大赛·线上初赛：带通配符的数
</font></a>

&nbsp;&nbsp;&nbsp;&nbsp;


<a href="http://bbs.csdn.net/topics/390724670"target="_blank">
<font color=red>CSDN社区“三八节”特别活动</font></a>
&nbsp;&nbsp;&nbsp;&nbsp;


<a href="http://www.csdn.net/article/2014-03-06/2818653"target="_blank">
<font color=blue>开发者职业生涯调查之未来</font></a>




</div>                    <link href="http://static.blog.csdn.net/css/comment1.css" type="text/css" rel="stylesheet" />
<link href="http://static.blog.csdn.net/css/style1.css" type="text/css" rel="stylesheet" />
<div id="article_details" class="details">
    <div class="article_title">
    <span class="ico ico_type_Original"></span>
    <h1>
        <span class="link_title"><a href="/luoshengyang/article/details/8303098">
        Android应用程序窗口（Activity）的绘图表面（Surface）的创建过程分析
        </a></span>
    </h1>
</div>

    <div class="article_manage">
        <span class="link_categories">
        分类：
            <a href="/Luoshengyang/article/category/838604">Android</a> 
        </span>
    <span class="link_postdate">2012-12-24 00:55</span>
    <span class="link_view" title="阅读次数">20031人阅读</span>
    <span class="link_comments" title="评论次数"><a href="#comments" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_pinglun'])">评论</a>(17)</span>
    <span class="link_collect"><a href="javascript:void(0);" onclick="javascript:_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_shoucang']);collectArticle('Android应用程序窗口（Activity）的绘图表面（Surface）的创建过程分析','8303098');return false;" title="收藏">收藏</a></span>
    <span class="link_report"><a href="#report"  onclick="javascript:_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_jubao']);report(8303098,2);return false;" title="举报">举报</a></span>
    
</div>
<div class="tag2box"><a href='http://www.csdn.net/tag/Activity' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">Activity</a><a href='http://www.csdn.net/tag/android' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">android</a><a href='http://www.csdn.net/tag/surface' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">surface</a><a href='http://www.csdn.net/tag/WindowManagerService' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">WindowManagerService</a><a href='http://www.csdn.net/tag/%e5%ba%94%e7%94%a8%e7%a8%8b%e5%ba%8f%e7%aa%97%e5%8f%a3' target=_blank onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">应用程序窗口</a></div>

    
<div id="article_content" class="article_content">
<p><span style="color:black;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在前文中，我们分析了应用程序窗口连接到</span><span style="color:black;">WindowManagerService</span><span style="color:black;">服务的过程。在这个过程中，</span><span style="color:black;">WindowManagerService</span><span style="color:black;">服务会为应用程序窗口创建过一个到</span><span style="color:black;">SurfaceFlinger</span><span style="color:black;">服务的连接。有了这个连接之后，</span><span style="color:black;">WindowManagerService</span><span style="color:black;">服务就可以为应用程序窗口创建绘图表面了，以便可以用来渲染窗口的</span><span style="color:black;">UI</span><span style="color:black;">。在本文中，我们就详细分析应用程序窗口的绘图表面的创建过程。</span></p><p><span style="color:black;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;从前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/7846923">Android应用程序与SurfaceFlinger服务的关系概述和学习计划</a>和<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8010977">Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划</a>这两个系列的文章可以知道，每一个在C++层实现的应用程序窗口都需要有一个绘图表面，然后才可以将自己的UI表现出来。这个绘图表面是需要由应用程序进程请求SurfaceFlinger服务来创建的，在SurfaceFlinger服务内部使用一个Layer对象来描述，同时，SurfaceFlinger服务会返回一个实现了ISurface接口的Binder本地对象给应用程序进程，于是，应用程序进程就可以获得一个实现了ISurface接口的Binder代理对象。有了这个实现了ISurface接口的Binder代理对象之后，在C++层实现的应用程序窗口就可以请求SurfaceFlinger服务分配图形缓冲区以及渲染已经填充好UI数据的图形缓冲区了。</span></p><p><span style="color:black;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;对于在Java层实现的Android应用程序窗口来说，它也需要请求SurfaceFlinger服务为它创建绘图表面，这个绘图表面使用一个Surface对象来描述。由于在Java层实现的Android应用程序窗口还要接受WindowManagerService服务管理，因此，它的绘图表面的创建流程就会比在C++层实现的应用程序窗口复杂一些。具体来说，就是在在Java层实现的Android应用程序窗口的绘图表面是通过两个Surface对象来描述，一个是在应用程序进程这一侧创建的，另一个是在WindowManagerService服务这一侧创建的，它们对应于SurfaceFlinger服务这一侧的同一个Layer对象，如图1所示：</span></p><p style="text-align: center;"><span style="color:black;"><img src="http://img.my.csdn.net/uploads/201212/19/1355850216_7048.jpg" alt="" /><br /></span></p><p style="text-align: center;"><span style="color:black;">图1 应用程序窗口的绘图表面的模型图</span></p><p><span style="color:black;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在应用程序进程这一侧，每一个应用程序窗口，即每一个Activity组件，都有一个关联的Surface对象，这个Surface对象是保在在一个关联的ViewRoot对象的成员变量mSurface中的，如图2所示：</span></p><p style="text-align: center;"><span style="color: black; "><img width="684" src="http://img.my.csdn.net/uploads/201211/17/1353087703_2343.jpg" alt="" /><br /></span></p><p style="text-align: center;"><span style="color: black; ">图2 应用程序窗口在应用程序进程这一侧的Surface的实现</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;图2的类关系图的详细描述可以参考前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8170307">Android应用程序窗口（Activity）实现框架简要介绍和学习计划</a>一文的图6，这里我们只关注Surface类的实现。在应用程序进程这一侧，每一个Java层的Surface对都对应有一个C++层的Surface对象，并且后者的地址值保存在前者的成员变量mNativeSurface中。C++层的Surface类的实现以及作用可以参考前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/7846923">Android应用程序与SurfaceFlinger服务的关系概述和学习计划</a>这个系列的文章。</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在WindowManagerService服务这一侧，每一个应用程序窗口，即每一个Activity组件，都有一个对应的WindowState对象，这个WindowState对象的成员变量mSurface同样是指向了一个Surface对象，如图3所示：</span></p><p style="text-align: center;"><img src="http://img.my.csdn.net/uploads/201211/17/1353087795_6634.jpg" alt="" /><br /></p><p style="text-align: center;">图3 应用程序窗口在WindowManagerService服务这一侧的Surface的实现</p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;图3的类关系图的详细描述可以参考前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8170307">Android应用程序窗口（Activity）实现框架简要介绍和学习计划</a>一文的图7，这里我们同样只关注Surface类的实现。在WindowManagerService服务这一侧，每一个Java层的Surface对都对应有一个C++层的SurfaceControl对象，并且后者的地址值保存在前者的成员变量mSurfaceControl中。C++层的SurfaceControl类的实现以及作用同样可以参考前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/7846923">Android应用程序与SurfaceFlinger服务的关系概述和学习计划</a>这个系列的文章。</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; 一个应用程序窗口分别位于应用程序进程和WindowManagerService服务中的两个Surface对象有什么区别呢？虽然它们都是用来操作位于SurfaceFlinger服务中的同一个Layer对象的，不过，它们的操作方式却不一样。具体来说，就是位于应用程序进程这一侧的Surface对象负责绘制应用程序窗口的UI，即往应用程序窗口的图形缓冲区填充UI数据，而位于WindowManagerService服务这一侧的Surface对象负责设置应用程序窗口的属性，例如位置、大小等属性。这两种不同的操作方式分别是通过C++层的Surface对象和SurfaceControl对象来完成的，因此，位于应用程序进程和WindowManagerService服务中的两个Surface对象的用法是有区别的。之所以会有这样的区别，是因为绘制应用程序窗口是独立的，由应用程序进程来完即可，而设置应用程序窗口的属性却需要全局考虑，即需要由WindowManagerService服务来统筹安排，例如，一个应用程序窗口的Z轴坐标大小要考虑它到的窗口类型以及它与系统中的其它窗口的关系。</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;说到这里，另外一个问题又来了，由于一个应用程序窗口对应有两个Surface对象，那么它们是如何创建出来的呢？简单地说，就是按照以下步骤来创建：</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; 1. 应用程序进程请求WindowManagerService服务为一个应用程序窗口创建一个Surface对象；</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; 2. WindowManagerService服务请求SurfaceFlinger服务创建一个Layer对象，并且获得一个ISurface接口；</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; 3.&nbsp;WindowManagerService服务将获得的ISurface接口保存在其内部的一个Surface对象中，并且将该ISurface接口返回给应用程序进程；</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; 4. 应用程序进程得到WindowManagerService服务返回的ISurface接口之后，再将其封装成其内部的另外一个Surface对象中。</span></p><p style="text-align: left;"><span style="color: black; ">&nbsp;&nbsp; &nbsp; &nbsp; 那么应用程序窗口的绘图表面又是什么时候创建的呢？一般是在不存在的时候就创建，因为应用程序窗口在运行的过程中，它的绘图表面会根据需要来销毁以及重新创建的，例如，应用程序窗口在第一次显示的时候，就会请求WindowManagerService服务为其创建绘制表面。从前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8245546">Android应用程序窗口（Activity）的视图对象（View）的创建过程分析</a>一文可以知道，当一个应用程序窗口被激活并且它的视图对象创建完成之后，应用程序进程就会调用与其所关联的一个ViewRoot对象的成员函数requestLayout来请求对其UI进行布局以及显示。由于这时候应用程序窗口的绘图表面尚未创建，因此，ViewRoot类的成员函数requestLayout就会请求WindowManagerService服务来创建绘图表面。接下来，我们就从ViewRoot类的成员函数requestLayout开始，分析应用程序窗口的绘图表面的创建过程，如图4所示：</span></p><p style="text-align: center;"><span style="color: black; "><img width="614" src="http://img.my.csdn.net/uploads/201212/20/1355933934_6913.jpg" alt="" /><br /></span></p><p style="text-align: center;">图4 应用程序窗口的绘图表面的创建过程</p><p style="text-align: left;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个过程可以分为10个步骤，接下来我们就详细分析每一个步骤。</p><p style="text-align: left;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 1. ViewRoot.requestLayout</p><p style="text-align: left;"></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_1_6598375"  code_snippet_id="129039" snippet_file_name="blog_20131226_1_6598375" name="code" class="java">public final class ViewRoot extends Handler implements ViewParent,
        View.AttachInfo.Callbacks {
    ......

    boolean mLayoutRequested;
    ......

    public void requestLayout() {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }

    ......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。<br /><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;ViewRoot类的成员函数requestLayout首先调用另外一个成员函数checkThread来检查当前线程是否就是创建当前正在处理的ViewRoot对象的线程。如果不是的话，那么ViewRoot类的成员函数checkThread就会抛出一个异常出来。ViewRoot类是从Handler类继承下来的，用来处理应用程序窗口的UI布局和渲染等消息。由于这些消息都是与Ui相关的，因此它们就需要在UI线程中处理，这样我们就可以推断出当前正在处理的ViewRoot对象是要应用程序进程的UI线程中创建的。进一步地，我们就可以推断出ViewRoot类的成员函数checkThread实际上就是用来检查当前线程是否是应用程序进程的UI线程，如果不是的话，它就会抛出一个异常出来。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;通过了上述检查之后，ViewRoot类的成员函数requestLayout首先将其成员变量mLayoutRequested的值设置为true，表示应用程序进程的UI线程正在被请求执行一个UI布局操作，接着再调用另外一个成员函数scheduleTraversals来继续执行UI布局的操作。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 2.&nbsp;ViewRoot.scheduleTraversals</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_2_8310242"  code_snippet_id="129039" snippet_file_name="blog_20131226_2_8310242" name="code" class="java">public final class ViewRoot extends Handler implements ViewParent,
        View.AttachInfo.Callbacks {
    ......
 
    boolean mTraversalScheduled;
    ......

    public void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            sendEmptyMessage(DO_TRAVERSAL);
        }
    }

    ......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;ViewRoot类的成员变量mTraversalScheduled用来表示应用程序进程的UI线程是否已经调度了一个DO_TRAVERSAL消息。如果已经调度了的话，它的值就会等于true。在这种情况下，&nbsp;ViewRoot类的成员函数scheduleTraversals就什么也不做，否则的话，它就会首先将成员变量mTraversalScheduled的值设置为true，然后再调用从父类Handler继承下来的成员函数sendEmptyMessage来往应用程序进程的UI线程发送一个DO_TRAVERSAL消息。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个类型为DO_TRAVERSAL的消息是由ViewRoot类的成员函数performTraversals来处理的，因此，接下来我们就继续分析ViewRoot类的成员函数performTraversals的实现。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 3.&nbsp;ViewRoot.performTraversals</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_3_7928558"  code_snippet_id="129039" snippet_file_name="blog_20131226_3_7928558" name="code" class="java">public final class ViewRoot extends Handler implements ViewParent,
        View.AttachInfo.Callbacks {
    ......

    View mView;
    ......

    boolean mLayoutRequested;
    boolean mFirst;
    ......
    boolean mFullRedrawNeeded;
    ......

    private final Surface mSurface = new Surface();
    ......

    private void performTraversals() {
        ......

        final View host = mView;
        ......

        mTraversalScheduled = false;
        ......
        boolean fullRedrawNeeded = mFullRedrawNeeded;
        boolean newSurface = false;
        ......

        if (mLayoutRequested) {
            ......

            host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            
            .......
        }

        ......

        int relayoutResult = 0;
        if (mFirst || windowShouldResize || insetsChanged
                || viewVisibilityChanged || params != null) {
            ......

            boolean hadSurface = mSurface.isValid();
            try {
                ......

                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
                ......

                if (!hadSurface) {
                    if (mSurface.isValid()) {
                        ......
                        newSurface = true;
                        fullRedrawNeeded = true;
                        ......
                    }
                } 
                ......
            } catch (RemoteException e) {
            }

            ......
        }

        final boolean didLayout = mLayoutRequested;
        ......

        if (didLayout) {
            mLayoutRequested = false;
            ......

            host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);

            ......
        }

        ......

        mFirst = false;
        ......

        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();

        if (!cancelDraw &amp;&amp; !newSurface) {
            mFullRedrawNeeded = false;
            draw(fullRedrawNeeded);

            ......
        } else {
            ......

            // Try again
            scheduleTraversals();
        }
    }

    ......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;ViewRoot类的成员函数performTraversals的实现是相当复杂的，这里我们分析它的实现框架，在以后的文章中，我们再详细分析它的实现细节。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在分析ViewRoot类的成员函数performTraversals的实现框架之前，我们首先了解ViewRoot类的以下五个成员变量：</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;<strong>--mView</strong>：它的类型为View，但它实际上指向的是一个DecorView对象，用来描述应用程序窗口的顶级视图，这一点可以参考前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8245546">Android应用程序窗口（Activity）的视图对象（View）的创建过程分析</a>一文。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;<strong>--mLayoutRequested</strong>：这是一个布尔变量，用来描述应用程序进程的UI线程是否需要正在被请求执行一个UI布局操作。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;<strong>--mFirst</strong>：这是一个布尔变量，用来描述应用程序进程的UI线程是否第一次处理一个应用程序窗口的UI。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>&nbsp;--mFullRedrawNeeded</strong>：这是一个布尔变量，用来描述应用程序进程的UI线程是否需要将一个应用程序窗口的全部区域都重新绘制。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;<strong>--mSurface</strong>：它指向一个Java层的Surface对象，用来描述一个应用程序窗口的绘图表面。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;注意，成员变量mSurface所指向的Surface对象在创建的时候，还没有在C++层有一个关联的Surface对象，因此，这时候它描述的就是一个无效的绘图表面。另外，这个Surface对象在应用程序窗口运行的过程中，也会可能被销毁，因此，这时候它描述的绘图表面也会变得无效。在上述两种情况中，我们都需要请求WindowManagerService服务来为当前正在处理的应用程序窗口创建有一个有效的绘图表面，以便可以在上面渲染UI。这个创建绘图表面的过程正是本文所要关心的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;理解了上述五个成员变量之后，我们就可以分析ViewRoot类的成员函数performTraversals的实现框架了，如下所示：</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;1. 将成员变量mView和mFullRedrawNeeded的值分别保存在本地变量host和fullRedrawNeeded中，并且将成员变量mTraversalScheduled的值设置为false，表示应用程序进程的UI线程中的DO_TRAVERSAL消息已经被处理了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;2. 本地变量newSurface用来描述当前正在处理的应用程序窗口在本轮的DO_TRAVERSAL消息处理中是否新创建了一个绘图表面，它的初始值为false。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;3. 如果成员变量mLayoutRequested的值等于true，那么就表示应用程序进程的UI线程正在被请求对当前正在处理的应用程序窗口执行一个UI布局操作，因此，这时候就会调用本地变量host所描述的一个顶层视图对象的成员函数measure来测量位于各个层次的UI控件的大小。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;4. 如果当前正在处理的应用程序窗口的UI是第一次被处理，即成员变量mFirst的值等于true，或者当前正在处理的应用程序窗口的大小发生了变化，即本地变量windowShouldResize的值等于true，或者当前正在处理的应用程序窗口的边衬发生了变化，即本地变量insetsChanged的值等于true，或者正在处理的应用程序窗口的可见性发生了变化，即本地变量viewVisibilityChanged的值等于true，或者正在处理的应用程序窗口的UI布局参数发生了变化，即本地变量params指向了一个WindowManager.LayoutParams对象，那么应用程序进程的UI线程就会调用另外一个成员函数relayoutWindow来请求WindowManagerService服务重新布局系统中的所有窗口。WindowManagerService服务在重新布局系统中的所有窗口的过程中，如果发现当前正在处理的应用程序窗口尚未具有一个有效的绘图表面，那么就会为它创建一个有效的绘图表面，这一点是我们在本文中所要关注的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;5. 应用程序进程的UI线程在调用ViewRoot类的成员函数relayoutWindow来请求WindowManagerService服务重新布局系统中的所有窗口之前，会调用成员变量mSurface所指向的一个Surface对象的成员函数isValid来判断它描述的是否是一个有效的绘图表面，并且将结果保存在本地变量hadSurface中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;6.&nbsp;应用程序进程的UI线程在调用ViewRoot类的成员函数relayoutWindow来请求WindowManagerService服务重新布局系统中的所有窗口之后，又会继续调用成员变量mSurface所指向的一个Surface对象的成员函数isValid来判断它描述的是否是一个有效的绘图表面。如果这时候成员变量mSurface所指向的一个Surface对象描述的是否是一个有效的绘图表面，并且本地变量hadSurface的值等于false，那么就说明WindowManagerService服务为当前正在处理的应用程序窗口新创建了一个有效的绘图表面，于是就会将本地变量newSurface和fullRedrawNeeded的值均修改为true。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;7.&nbsp;应用程序进程的UI线程再次判断mLayoutRequested的值是否等于true。如果等于的话，那么就说明需要对当前正在处理的应用程序窗口的UI进行重新布局，这是通过调用本地变量host所描述的一个顶层视图对象的成员函数layout来实现的。在对当前正在处理的应用程序窗口的UI进行重新布局之前，应用程序进程的UI线程会将成员变量mLayoutRequested的值设置为false，表示之前所请求的一个UI布局操作已经得到处理了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;8.&nbsp;应用程序进程的UI线程接下来就要开始对当前正在处理的应用程序窗口的UI进行重新绘制了，不过在重绘之前，会先询问一下那些注册到当前正在处理的应用程序窗口中的Tree Observer，即调用它们的成员函数dispatchOnPreDraw，看看它们是否需要取消接下来的重绘操作，这个询问结果保存在本地变量cancelDraw中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;9. 如果本地变量cancelDraw的值等于false，并且本地变量newSurface的值也等于false，那么就说明注册到当前正在处理的应用程序窗口中的Tree Observer不要求取消当前的这次重绘操作，并且当前正在处理的应用程序窗口也没有获得一个新的绘图表面。在这种情况下，应用程序进程的UI线程就会调用ViewRoot类的成员函数draw来对当前正在处理的应用程序窗口的UI进行重绘。在重绘之前，还会将ViewRoot类的成员变量mFullRedrawNeeded的值重置为false。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 10.&nbsp;如果本地变量cancelDraw的值等于true，或者本地变量newSurface的值等于true，那么就说明注册到当前正在处理的应用程序窗口中的Tree Observer要求取消当前的这次重绘操作，或者当前正在处理的应用程序窗口获得了一个新的绘图表面。在这两种情况下，应用程序进程的UI线程就不能对当前正在处理的应用程序窗口的UI进行重绘了，而是要等到下一个DO_TRAVERSAL消息到来的时候，再进行重绘，以便使得当前正在处理的应用程序窗口的各项参数可以得到重新设置。下一个DO_TRAVERSAL消息需要马上被调度，因此，应用程序进程的UI线程就会重新执行ViewRoot类的成员函数scheduleTraversals。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这样，我们就分析完成ViewRoot类的成员函数performTraversals的实现框架了，接下来我们就继续分析ViewRoot类的成员函数relayoutWindow的实现，以便可以看到当前正在处理的应用程序窗口的绘图表面是如何创建的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 4.&nbsp;ViewRoot.relayoutWindow</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_4_8834412"  code_snippet_id="129039" snippet_file_name="blog_20131226_4_8834412" name="code" class="java">public final class ViewRoot extends Handler implements ViewParent,  
        View.AttachInfo.Callbacks {  
    ......  
  
    static IWindowSession sWindowSession;  
    ......  

    final W mWindow;
    ......
  
    private final Surface mSurface = new Surface();  
    ......  
  
    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,  
            boolean insetsPending) throws RemoteException {  
        ......  
  
        int relayoutResult = sWindowSession.relayout(  
                mWindow, params,  
                (int) (mView.mMeasuredWidth * appScale + 0.5f),  
                (int) (mView.mMeasuredHeight * appScale + 0.5f),  
                viewVisibility, insetsPending, mWinFrame,  
                mPendingContentInsets, mPendingVisibleInsets,  
                mPendingConfiguration, mSurface);  
        ......  
  
        return relayoutResult;  
    }  
  
    ......  
}  </pre><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;ViewRoot类的成员函数relayoutWindow调用静态成员变量sWindowSession所描述的一个实现了IWindowSession接口的Binder代理对象的成员函数relayout来请求WindowManagerService服务对成员变量mWindow所描述的一个应用程序窗口的UI进行重新布局，同时，还会将成员变量mSurface所描述的一个Surface对象传递给WindowManagerService服务，以便WindowManagerService服务可以根据需要来重新创建一个绘图表面给成员变量mWindow所描述的一个应用程序窗口使用。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 实现了IWindowSession接口的Binder代理对象是由IWindowSession.Stub.Proxy类来描述的，接下来我们就继续分析它的成员函数relayout的实现。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 5.&nbsp;IWindowSession.Stub.Proxy.relayout</p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;IWindowSession接口是使用AIDL语言来描述的，如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_5_9324780"  code_snippet_id="129039" snippet_file_name="blog_20131226_5_9324780" name="code" class="java">interface IWindowSession {
    ......

    int relayout(IWindow window, in WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility,
            boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
            out Rect outVisibleInsets, out Configuration outConfig,
            out Surface outSurface);

    ......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个接口定义在frameworks/base/core/java/android/view/IWindowSession.aidl文件中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;使用AIDL语言来描述的IWindowSession接口被编译后，就会生成一个使用Java语言来描述的IWindowSession.Stub.Proxy类，它的成员函数relayout的实现如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_6_8943096"  code_snippet_id="129039" snippet_file_name="blog_20131226_6_8943096" name="code" class="java">public interface IWindowSession extends android.os.IInterface
{
    ......

    public static abstract class Stub extends android.os.Binder implements android.view.IWindowSession
    {
         ......

         private static class Proxy implements android.view.IWindowSession
         {
             private android.os.IBinder mRemote;
             ......

             public int relayout(android.view.IWindow window, android.view.WindowManager.LayoutParams attrs, 
                   int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending, 
                   android.graphics.Rect outFrame, 
                   android.graphics.Rect outContentInsets, 
                   android.graphics.Rect outVisibleInsets, 
                   android.content.res.Configuration outConfig, 
                   android.view.Surface outSurface) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();

                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));

                    if ((attrs!=null)) {
                        _data.writeInt(1);
                        attrs.writeToParcel(_data, 0);
                    }
                    else {
                        _data.writeInt(0);
                    }

                    _data.writeInt(requestedWidth);
                    _data.writeInt(requestedHeight);
                    _data.writeInt(viewVisibility);
                    _data.writeInt(((insetsPending)?(1):(0)));
                   
                    mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
                
                    _reply.readException();
                    _result = _reply.readInt();

                    if ((0!=_reply.readInt())) {
                        outFrame.readFromParcel(_reply);
                    }

                    if ((0!=_reply.readInt())) {
                        outContentInsets.readFromParcel(_reply);
                    }

                    if ((0!=_reply.readInt())) {
                        outVisibleInsets.readFromParcel(_reply);
                    }

                    if ((0!=_reply.readInt())) {
                        outConfig.readFromParcel(_reply);
                    }

                    if ((0!=_reply.readInt())) {
                        outSurface.readFromParcel(_reply);
                    }

                } finally {
                    _reply.recycle();
                    _data.recycle();
                }

                return _result;
            }

            ......
        }

        ......
    }

    ......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowSession.java中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;IWindowSession.Stub.Proxy类的成员函数relayout首先将从前面传进来的各个参数写入到Parcel对象_data中，接着再通过其成员变量mRemote所描述的一个Binder代理对象向运行在WindowManagerService服务内部的一个Session对象发送一个类型为TRANSACTION_relayout的进程间通信请求，其中，这个Session对象是用来描述从当前应用程序进程到WindowManagerService服务的一个连接的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;当运行在WindowManagerService服务内部的Session对象处理完成当前应用程序进程发送过来的类型为TRANSACTION_relayout的进程间通信请求之后，就会将处理结果写入到Parcel对象_reply中，并且将这个Parcel对象_reply返回给当前应用程序进程处理。返回结果包含了一系列与参数window所描述的应用程序窗口相关的参数，如下所示：</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 1. 窗口的大小：最终保存在输出参数outFrame中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 2. 窗口的内容区域边衬大小：最终保存在输出参数outContentInsets中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 3. 窗口的可见区域边衬大小：最终保存在输出参数outVisibleInsets中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 4. 窗口的配置信息：最终保存在输出参数outConfig中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 5. 窗口的绘图表面：最终保存在输出参数outSurface中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这里我们只关注从WindowManagerService服务返回来的窗口绘图表面是如何保存到输出参数outSurface中的，即关注Surface类的成员函数readFromParcel的实现。从前面的调用过程可以知道，输出参数outSurface描述的便是当前正在处理的应用程序窗口的绘图表面，将WindowManagerService服务返回来的窗口绘图表面保存在它里面，就相当于是为当前正在处理的应用程序窗口创建了一个绘图表面。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 在分析Surface类的成员函数readFromParcel的实现之前，我们先分析Session类的成员函数relayout的实现，因为它是用来处理类型为TRANSACTION_relayout的进程间通信请求的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 6.&nbsp;Session.relayout</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_7_721004"  code_snippet_id="129039" snippet_file_name="blog_20131226_7_721004" name="code" class="java">public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor {
    ......

    private final class Session extends IWindowSession.Stub
            implements IBinder.DeathRecipient {
        ......

        public int relayout(IWindow window, WindowManager.LayoutParams attrs,
                int requestedWidth, int requestedHeight, int viewFlags,
                boolean insetsPending, Rect outFrame, Rect outContentInsets,
                Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
            //Log.d(TAG, &quot;&gt;&gt;&gt;&gt;&gt;&gt; ENTERED relayout from &quot; + Binder.getCallingPid());
            int res = relayoutWindow(this, window, attrs,
                    requestedWidth, requestedHeight, viewFlags, insetsPending,
                    outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
            //Log.d(TAG, &quot;&lt;&lt;&lt;&lt;&lt;&lt; EXITING relayout to &quot; + Binder.getCallingPid());
            return res;
        }

        ......

    }

    ......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Session类的成员函数relayout调用了外部类WindowManagerService的成员函数relayoutWindow来对参数参数window所描述的一个应用程序窗口的UI进行布局，接下来我们就继续分析WindowManagerService类的成员函数relayoutWindow的实现。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 7.&nbsp;WindowManagerService.relayoutWindow</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_8_6402605"  code_snippet_id="129039" snippet_file_name="blog_20131226_8_6402605" name="code" class="java">public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor {
    ......

    public int relayoutWindow(Session session, IWindow client,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, boolean insetsPending,
            Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
            Configuration outConfig, Surface outSurface) {
        ......

        synchronized(mWindowMap) {
            WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }

            if (viewVisibility == View.VISIBLE &amp;&amp;
                    (win.mAppToken == null || !win.mAppToken.clientHidden)) {
                ......

                try {
                    Surface surface = win.createSurfaceLocked();
                    if (surface != null) {
                        outSurface.copyFrom(surface);
                        ......
                    } else {
                        // For some reason there isn't a surface.  Clear the
                        // caller's object so they see the same state.
                        outSurface.release();
                    }
                } catch (Exception e) {
                    ......
                    return 0;
                }
               
                ......
            }

            ......
        }

        return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)
                | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0);
    }
    
    ......
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;WindowManagerService类的成员函数relayoutWindow的实现是相当复杂的，这里我们只关注与创建应用程序窗口的绘图表面相关的代码，在后面的文章中，我们再详细分析它的实现。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;简单来说，WindowManagerService类的成员函数relayoutWindow根据应用程序进程传递过来的一系列数据来重新设置由参数client所描述的一个应用程序窗口的大小和可见性等信息，而当一个应用程序窗口的大小或者可见性发生变化之后，系统中当前获得焦点的窗口，以及输入法窗口和壁纸窗口等都可能会发生变化，而且也会对其它窗口产生影响，因此，这时候WindowManagerService类的成员函数relayoutWindow就会对系统中的窗口的布局进行重新调整。对系统中的窗口的布局进行重新调整的过程是整个WindowManagerService服务最为复杂和核心的内容，我们同样是在后面的文章中再详细分析。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;现在，我们就主要分析参数client所描述的一个应用程序窗口的绘图表面的创建过程。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;WindowManagerService类的成员函数relayoutWindow首先获得与参数client所对应的一个WindowState对象win，这是通过调用WindowManagerService类的成员函数windowForClientLocked来实现的。如果这个对应的WindowState对象win不存在，那么就说明应用程序进程所请求处理的应用程序窗口不存在，这时候WindowManagerService类的成员函数relayoutWindow就直接返回一个0值给应用程序进程。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;WindowManagerService类的成员函数relayoutWindow接下来判断参数client所描述的一个应用程序窗口是否是可见的。一个窗口只有在可见的情况下，WindowManagerService服务才会为它创建一个绘图表面。&nbsp;一个窗口是否可见由以下两个条件决定：</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;1. 参数viewVisibility的值等于View.VISIBLE，表示应用程序进程请求将它设置为可见的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;2.&nbsp;WindowState对象win的成员变量mAppToken不等于null，并且它所描述的一个AppWindowToken对象的成员变量clientHidden的值等于false。这意味着参数client所描述的窗口是一个应用程序窗口，即一个Activity组件窗口，并且这个Activity组件当前是处于可见状态的。当一个Activity组件当前是处于不可见状态时，它的窗口就也必须是处于不可见状态。WindowState类的成员变量mAppToken的具体描述可以参考前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8275938">Android应用程序窗口（Activity）与WindowManagerService服务的连接过程分析</a>一文。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;注意，当WindowState对象win的成员变量mAppToken等于null时，只要满足条件1就可以了，因为这时候参数client所描述的窗口不是一个Activity组件窗口，它的可见性不像Activity组件窗口一样受到Activity组件的可见性影响。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;我们假设参数client所描述的是一个应用程序窗口，并且这个应用程序窗口是可见的，那么WindowManagerService类的成员函数relayoutWindow接下来就会调用WindowState对象win的成员函数createSurfaceLocked来为它创建一个绘图表面。如果这个绘图表面能创建成功，那么WindowManagerService类的成员函数relayoutWindow就会将它的内容拷贝到输出参数outSource所描述的一个Surface对象去，以便可以将它返回给应用程序进程处理。另一方面，如果这个绘图表面不能创建成功，那么WindowManagerService类的成员函数relayoutWindow就会将输出参数outSource所描述的一个Surface对象的内容释放掉，以便应用程序进程知道该Surface对象所描述的绘图表面已经失效了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;接下来，我们就继续分析WindowState类的成员函数createSurfaceLocked的实现，以便可以了解一个应用程序窗口的绘图表面的创建过程。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Step 8. WindowState.createSurfaceLocked</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_9_4069074"  code_snippet_id="129039" snippet_file_name="blog_20131226_9_4069074" name="code" class="java">public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor {
    ......

    private final class WindowState implements WindowManagerPolicy.WindowState {
        ......

        Surface mSurface;
        ......
    
        Surface createSurfaceLocked() {
            if (mSurface == null) {
                mReportDestroySurface = false;
                mSurfacePendingDestroy = false;
                mDrawPending = true;
                mCommitDrawPending = false;
                mReadyToShow = false;
                if (mAppToken != null) {
                    mAppToken.allDrawn = false;
                }

                int flags = 0;
                if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
                    flags |= Surface.PUSH_BUFFERS;
                }

                if ((mAttrs.flags&amp;WindowManager.LayoutParams.FLAG_SECURE) != 0) {
                    flags |= Surface.SECURE;
                }

                ......

                int w = mFrame.width();
                int h = mFrame.height();
                if ((mAttrs.flags &amp; LayoutParams.FLAG_SCALED) != 0) {
                    // for a scaled surface, we always want the requested
                    // size.
                    w = mRequestedWidth;
                    h = mRequestedHeight;
                }

                // Something is wrong and SurfaceFlinger will not like this,
                // try to revert to sane values
                if (w &lt;= 0) w = 1;
                if (h &lt;= 0) h = 1;

                mSurfaceShown = false;
                mSurfaceLayer = 0;
                mSurfaceAlpha = 1;
                mSurfaceX = 0;
                mSurfaceY = 0;
                mSurfaceW = w;
                mSurfaceH = h;
                try {
                    mSurface = new Surface(
                            mSession.mSurfaceSession, mSession.mPid,
                            mAttrs.getTitle().toString(),
                            0, w, h, mAttrs.format, flags);
                    ......
                } catch (Surface.OutOfResourcesException e) {
                    ......
                    reclaimSomeSurfaceMemoryLocked(this, &quot;create&quot;);
                    return null;
                } catch (Exception e) {
                    ......
                    return null;
                }
                Surface.openTransaction();
                try {
                    try {
                        mSurfaceX = mFrame.left + mXOffset;
                        mSurfaceY = mFrame.top + mYOffset;
                        mSurface.setPosition(mSurfaceX, mSurfaceY);
                        mSurfaceLayer = mAnimLayer;
                        mSurface.setLayer(mAnimLayer);
                        mSurfaceShown = false;
                        mSurface.hide();
                        if ((mAttrs.flags&amp;WindowManager.LayoutParams.FLAG_DITHER) != 0) {
                            ......
                            mSurface.setFlags(Surface.SURFACE_DITHER,
                                    Surface.SURFACE_DITHER);
                        }
                    } catch (RuntimeException e) {
                        ......
                        reclaimSomeSurfaceMemoryLocked(this, &quot;create-init&quot;);
                    }
                    mLastHidden = true;
                } finally {
                    ......
                    Surface.closeTransaction();
                }
                ......
            }
            return mSurface;
        }

        ......
    }

    ......
}</pre><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在创建一个应用程序窗口的绘图表面之前，我们需要知道以下数据：</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;1. 应用程序窗口它所运行的应用程序进程的PID。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;2. 与应用程序窗口它所运行的应用程序进程所关联的一个SurfaceSession对象。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;3.&nbsp;应用程序窗口的标题。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;4. 应用程序窗口的像素格式。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;5.&nbsp;应用程序窗口的宽度。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;6.&nbsp;应用程序窗口的高度。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;7. 应用程序窗口的图形缓冲区属性标志。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;第1个和第2个数据可以通过当前正在处理的WindowState对象的成员变量mSession所描述的一个Session对象的成员变量mPid和mSurfaceSession来获得；第3个和第4个数据可以通过当前正在处理的WindowState对象的成员变量mAttr所描述的一个WindowManager.LayoutParams对象的成员函数getTitle以及成员变量format来获得；接下来我们就分析后面3个数据是如何获得的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;WindowState类的成员变量mFrame的类型为Rect，它用来描述应用程序窗口的位置和大小，它们是由WindowManagerService服务根据屏幕大小以及其它属性计算出来的，因此，通过调用过它的成员函数width和height就可以得到要创建绘图表面的应用程序窗口的宽度和高度，并且保存在变量w和h中。但是，当当前正在处理的WindowState对象的成员变量mAttr所描述的一个WindowManager.LayoutParams对象的成员变量flags的LayoutParams.FLAG_SCALED位不等于0时，就说明应用程序进程指定了该应用程序窗口的大小，这时候指定的应用程序窗口的宽度和高度就保存在WindowState类的成员变量mRequestedWidth和mRequestedHeight中，因此，我们就需要将当前正在处理的WindowState对象的成员变量mRequestedWidth和mRequestedHeight的值分别保存在变量w和h中。经过上述两步计算之后，如果得到的变量w和h等于0，那么就说明当前正在处理的WindowState对象所描述的应用程序窗口的大小还没有经过计算，或者还没有被指定过，这时候就需要将它们的值设置为1，避免接下来创建一个大小为0的绘图表面。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;如果当前正在处理的WindowState对象的成员变量mAttr所描述的一个WindowManager.LayoutParams对象的成员变量memoryType的值等于MEMORY_TYPE_PUSH_BUFFERS，那么就说明正在处理的应用程序窗口不拥有专属的图形缓冲区，这时候就需要将用来描述正在处理的应用程序窗口的图形缓冲区属性标志的变量flags的Surface.PUSH_BUFFERS位设置为1。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 此外，如果当前正在处理的WindowState对象的成员变量mAttr所描述的一个WindowManager.LayoutParams对象的成员变量flags的WindowManager.LayoutParams.FLAG_SECURE位不等于0，那么就说明正在处理的应用程序窗口的界面是安全的，即是受保护的，这时候就需要将用来描述正在处理的应用程序窗口的图形缓冲区属性标志的变量flags的Surface.SECURE位设置为1。当一个应用程序窗口的界面是受保护时，SurfaceFlinger服务在执行截屏功能时，就不能把它的界面截取下来。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 上述数据准备就绪之后，就可以创建当前正在处理的WindowState对象所描述的一个应用程序窗口的绘图表面了，不过在创建之前，还会初始化该WindowState对象的以下成员变量：</p><p>&nbsp;&nbsp; &nbsp; &nbsp;<strong> --mReportDestroySurface的值被设置为false</strong>。当一个应用程序窗口的绘图表面被销毁时，WindowManagerService服务就会将相应的WindowState对象的成员变量mReportDestroySurface的值设置为true，表示要向该应用程序窗口所运行在应用程序进程发送一个绘图表面销毁通知。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;<strong> --mSurfacePendingDestroy的值被设置为false</strong>。当一个应用程序窗口的绘图表面正在等待销毁时，WindowManagerService服务就会将相应的WindowState对象的成员变量mReportDestroySurface的值设置为true。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>--mDrawPending的值被设置为true</strong>。当一个应用程序窗口的绘图表面处于创建之后并且绘制之前时，WindowManagerService服务就会将相应的WindowState对象的成员变量mDrawPending的值设置为true，以表示该应用程序窗口的绘图表面正在等待绘制。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>--mCommitDrawPending的值被设置为false</strong>。当一个应用程序窗口的绘图表面绘制完成之后并且可以显示出来之前时，WindowManagerService服务就会将相应的WindowState对象的成员变量mCommitDrawPending的值设置为true，以表示该应用程序窗口正在等待显示。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>--mReadyToShow的值被设置为fals</strong>e。有时候当一个应用程序窗口的绘图表面绘制完成并且可以显示出来之后，由于与该应用程序窗口所关联的一个Activity组件的其它窗口还未准备好显示出来，这时候WindowManagerService服务就会将相应的WindowState对象的成员变量mReadyToShow的值设置为true，以表示该应用程序窗口需要延迟显示出来，即需要等到与该应用程序窗口所关联的一个Activity组件的其它窗口也可以显示出来之后再显示。</p><p>&nbsp;&nbsp; &nbsp; <strong>&nbsp;&nbsp;--如果成员变量mAppToken的值不等于null，那么就需要将它所描述的一个AppWindowToken对象的成员变量allDrawn的值设置为false</strong>。从前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8275938">Android应用程序窗口（Activity）与WindowManagerService服务的连接过程分析</a>一文可以知道，一个AppWindowToken对象用来描述一个Activity组件的，当该AppWindowToken对象的成员变量allDrawn的值等于true时，就表示属于该Activity组件的所有窗口都已经绘制完成了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>--mSurfaceShown的值被设置为false</strong>，表示应用程序窗口还没有显示出来，它是用来显示调试信息的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>--mSurfaceLayer的值被设置为0</strong>，表示应用程序窗口的Z轴位置，它是用来显示调试信息的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>--mSurfaceAlpha的值被设置为1</strong>，表示应用程序窗口的透明值，它是用来显示调试信息的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>--mSurfaceX的值被设置为0</strong>，表示应用程序程序窗口的X轴位置，它是用来显示调试信息的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;<strong>--mSurfaceY的值被设置为0</strong>，表示应用程序程序窗口的Y轴位置，它是用来显示调试信息的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; <strong>--mSurfaceW的值被设置为w</strong>，表示应用程序程序窗口的宽度，它是用来显示调试信息的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;<strong>--mSurfaceH的值被设置为h</strong>，表示应用程序程序窗口的高度，它是用来显示调试信息的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 一个应用程序窗口的绘图表面在创建完成之后，函数就会将得到的一个Surface对象保存在当前正在处理的WindowState对象的成员变量mSurface中。注意，如果创建绘图表面失败，并且从Surface类的构造函数抛出来的异常的类型为Surface.OutOfResourcesException，那么就说明系统当前的内存不足了，这时候函数就会调用WindowManagerService类的成员函数reclaimSomeSurfaceMemoryLocked来回收内存。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 如果一切正常，那么函数接下来还会设置当前正在处理的WindowState对象所描述应用程序窗口的以下属性：</p><p>&nbsp;&nbsp; &nbsp; &nbsp;1. X轴和Y轴位置。前面提到，WindowState类的成员变量mFrame是用来描述应用程序窗口的位置和大小的，其中，位置就是通过它所描述的一个Rect对象的成员变量left和top来表示的，它们分别应用窗口在X轴和Y轴上的位置。此外，当一个WindowState对象所描述的应用程序窗口是一个壁纸窗口时，该WindowState对象的成员变量mXOffset和mYOffset用来描述壁纸窗口相对当前要显示的窗口在X轴和Y轴上的偏移量。因此，将WindowState类的成员变量mXOffset的值加上另外一个成员变量mFrame所描述的一个Rect对象的成员变量left的值，就可以得到一个应用程序窗口在X轴上的位置，同样，将WindowState类的成员变量mYOffset的值加上另外一个成员变量mFrame所描述的一个Rect对象的成员变量top的值，就可以得到一个应用程序窗口在Y轴上的位置。最终得到的位置值就分别保存在WindowState类的成员变量mSurfaceX和mSurfaceY，并且会调用WindowState类的成员变量mSurface所描述的一个Surface对象的成员函数setPosition来将它们设置到SurfaceFlinger服务中去。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;2. Z轴位置。在前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8275938">Android应用程序窗口（Activity）与WindowManagerService服务的连接过程分析</a>一文中提到，WindowState类的成员变量mAnimLayer用来描述一个应用程序窗口的Z轴位置，因此，这里就会先将它保存在WindowState类的另外一个成员变量mSurfaceLayer中，然后再调用WindowState类的成员变量mSurface所描述的一个Surface对象的成员函数setLayer来将它设置到SurfaceFlinger服务中去。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;3. 抖动标志。如果WindowState类的成员变量mAttr所描述的一个WindowManager.LayoutParams对象的成员变量flags的WindowManager.LayoutParams.FLAG_DITHER位不等于0，那么就说明一个应用程序窗口的图形缓冲区在渲染时，需要进行抖动处理，这时候就会调用WindowState类的成员变量mSurface所描述的一个Surface对象的成员函数setLayer来将对应的应用程序窗口的图形缓冲区的属性标志的Surface.SURFACE_DITHER位设置为1。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;4. 显示状态。由于当前正在处理的WindowState对象所描述的一个应用程序窗口的绘图表面刚刚创建出来，因此，我们就需要通知SurfaceFlinger服务将它隐藏起来，这是通过调用当前正在处理的WindowState对象的成员变量mSurface所描述的一个Surface对象的成员变量hide来实现的。这时候还会将当前正在处理的WindowState对象的成员变量mSurfaceShown和mLastHidden的值分别设置为false和true，以表示对应的应用程序窗口是处于隐藏状态的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;注意，为了避免SurfaceFlinger服务每设置一个应用程序窗口属性就重新渲染一次系统的UI，上述4个属性设置需要在一个事务中进行，这样就可以避免出现界面闪烁。我们通过调用Surface类的静态成员函数openTransaction和closeTransaction就可以分别在SurfaceFlinger服务中打开和关闭一个事务。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;还有另外一个地方需要注意的是，在设置应用程序窗口属性的过程中，如果抛出了一个RuntimeException异常，那么就说明系统当前的内存不足了，这时候函数也会调用WindowManagerService类的成员函数reclaimSomeSurfaceMemoryLocked来回收内存。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;接下来，我们就继续分析Surface类的构造函数的实现，以便可以了解一个应用程序窗口的绘图表面的详细创建过程。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 9. new Surface</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_10_1735542"  code_snippet_id="129039" snippet_file_name="blog_20131226_10_1735542" name="code" class="java">public class Surface implements Parcelable {
    ......

    private int mSurfaceControl;
    ......
    private Canvas mCanvas;
    ......
    private String mName;
    ......

    public Surface(SurfaceSession s,
            int pid, String name, int display, int w, int h, int format, int flags)
        throws OutOfResourcesException {
        ......

        mCanvas = new CompatibleCanvas();
        init(s,pid,name,display,w,h,format,flags);
        mName = name;
    }

    ......

    private native void init(SurfaceSession s,
            int pid, String name, int display, int w, int h, int format, int flags)
            throws OutOfResourcesException;

    ......
}</pre><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/core/java/android/view/Surface.java中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Surface类有三个成员变量mSurfaceControl、mCanvas和mName，它们的类型分别是int、Canvas和mName，其中，mSurfaceControl保存的是在C++层的一个SurfaceControl对象的地址值，mCanvas用来描述一块类型为CompatibleCanvas的画布，mName用来描述当前正在创建的一个绘图表面的名称。画布是真正用来绘制UI的地方，不过由于现在正在创建的绘图表面是在WindowManagerService服务这一侧使用的，而WindowManagerService服务不会去绘制应用程序窗口的UI，它只会去设置应用程序窗口的属性，因此，这里创建的画布实际上没有什么作用，我们主要关注与成员变量mSurfaceControl所关联的C++层的SurfaceControl对象是如何创建的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Surface类的构造函数是通过调用另外一个成员函数init来创建与成员变量mSurfaceControl所关联的C++层的SurfaceControl对象的。Surface类的成员函数init是一个JNI方法，它是由C++层的函数Surface_init来实现的，如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_11_1877246"  code_snippet_id="129039" snippet_file_name="blog_20131226_11_1877246" name="code" class="cpp">static void Surface_init(
        JNIEnv* env, jobject clazz,
        jobject session,
        jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
{
    if (session == NULL) {
        doThrow(env, &quot;java/lang/NullPointerException&quot;);
        return;
    }

    SurfaceComposerClient* client =
            (SurfaceComposerClient*)env-&gt;GetIntField(session, sso.client);

    sp&lt;SurfaceControl&gt; surface;
    if (jname == NULL) {
        surface = client-&gt;createSurface(pid, dpy, w, h, format, flags);
    } else {
        const jchar* str = env-&gt;GetStringCritical(jname, 0);
        const String8 name(str, env-&gt;GetStringLength(jname));
        env-&gt;ReleaseStringCritical(jname, str);
        surface = client-&gt;createSurface(pid, name, dpy, w, h, format, flags);
    }

    if (surface == 0) {
        doThrow(env, OutOfResourcesException);
        return;
    }
    setSurfaceControl(env, clazz, surface);
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/core/jni/android_view_Surface.cpp中。<br /><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;参数session指向了在Java层所创建的一个SurfaceSession对象。从前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/8275938">Android应用程序窗口（Activity）与WindowManagerService服务的连接过程分析</a>一文可以知道，Java层的SurfaceSession对象有一个成员变量mClient，它指向了在C++层中的一个SurfaceComposerClient对象。从前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/7846923">Android应用程序与SurfaceFlinger服务的关系概述和学习计划</a>这一系列的文章又可以知道，C++层的SurfaceComposerClient对象可以用来请求SurfaceFlinger服务为应用程序窗口创建绘图表面，即创建一个Layer对象。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;因此，函数首先将参数session所指向的一个Java层的SurfaceSession对象的成员变量mClient转换成一个SurfaceComposerClient对象，然后再调用这个SurfaceComposerClient对象的成员函数createSurface来请求SurfaceFlinger服务来为参数clazz所描述的一个Java层的Surface对象所关联的应用程序窗口创建一个Layer对象。SurfaceFlinger服务创建完成这个Layer对象之后，就会将该Layer对象内部的一个实现了ISurface接口的SurfaceLayer对象返回给函数，于是，函数就可以获得一个实现了ISurface接口的Binder代理对象。这个实现了ISurface接口的Binder代理对象被封装在C++层的一个SurfaceControl对象surface中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 注意，sso是一个全局变量，它是一个类型为sso_t的结构体，它的定义如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_12_7558847"  code_snippet_id="129039" snippet_file_name="blog_20131226_12_7558847" name="code" class="cpp">struct sso_t {
    jfieldID client;
};
static sso_t sso;</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;它的成员函数client用来描述Java层中的SurfaceSession类的成员变量mClient在类中的偏移量，因此，函数Surface_init通过这个偏移量就可以访问参数session所指向的一个SurfaceSession对象的成员变量mClient的值，从而获得一个SurfaceComposerClient对象。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 另外一个需要注意的地方是，当参数name的值等于null时，函数Surface_init调用前面所获得一个SurfaceComposerClient对象的六个参数版本的成员函数createSurface来请求SurfaceFlinger服务创建一个Layer对象，否则的话，就调用七个参数版本的成员函数createSurface来请求SurfaceFlinger服务创建一个Layer对象。SurfaceComposerClient类的成员函数createSurface的实现可以参考前面<a target=_blank target="_blank" href="http://blog.csdn.net/luoshengyang/article/details/7884628">Android应用程序请求SurfaceFlinger服务创建Surface的过程分析</a>一文。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 得到了SurfaceControl对象surface之后，函数Surface_init接下来继续调用另外一个函数setSurfaceControl来它的地址值保存在参数clazz所指向的一个Java层的Surface对象的成员变量mSurfaceControl中，如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_13_7177163"  code_snippet_id="129039" snippet_file_name="blog_20131226_13_7177163" name="code" class="cpp">static void setSurfaceControl(JNIEnv* env, jobject clazz,
        const sp&lt;SurfaceControl&gt;&amp; surface)
{
    SurfaceControl* const p =
        (SurfaceControl*)env-&gt;GetIntField(clazz, so.surfaceControl);
    if (surface.get()) {
        surface-&gt;incStrong(clazz);
    }
    if (p) {
        p-&gt;decStrong(clazz);
    }
    env-&gt;SetIntField(clazz, so.surfaceControl, (int)surface.get());
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/core/jni/android_view_Surface.cpp中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;在分析函数setSurfaceControl的实现之前，我们先分析全局变量so的定义，如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_14_7003222"  code_snippet_id="129039" snippet_file_name="blog_20131226_14_7003222" name="code" class="cpp">struct so_t {
    jfieldID surfaceControl;
    jfieldID surface;
    jfieldID saveCount;
    jfieldID canvas;
};
static so_t so;</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;它是一个类型为so_t的结构体。结构体so_t有四个成员变量surfaceControl、surface、saveCount和canvas，它们分别用来描述Java层的Surface类的四个成员变量mSurfaceControl、mNativeSurface、mSaveCount和mCanvas在类中的偏移量。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 回到函数setSurfaceControl中，它首先通过结构体so的成员变量surfaceControl来获得参数clazz所指向的一个Java层的Surface对象的成员变量mSurfaceControl所关联的一个C++层的SurfaceControl对象。如果这个SurfaceControl对象存在，那么变量p的值就不会等于null，在这种情况下，就需要调用它的成员函数decStrong来减少它的强引用计数，因为接下来参数clazz所指向的一个Java层的Surface对象不再通过成员变量mSurfaceControl来引用它了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 另一方面，函数setSurfaceControl需要增加参数surface所指向的一个C++层的SurfaceControl对象的强引用计数，即调用参数surface所指向的一个C++层的SurfaceControl对象的成员函数incStrong，因为接下来参数clazz所指向的一个Java层的Surface对象要通过成员变量mSurfaceControl来引用它，即将它的地址值保存在参数clazz所指向的一个Java层的Surface对象的成员变量mSurfaceControl中。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 这一步执行完成之后，沿着调用路径层层返回，回到前面的Step 8中，即WindowState类的成员函数createSurfaceLocked中，这时候一个绘图表面就创建完成了。这个绘图表面最终会返回给请求创建它的应用程序进程，即前面的Step 5，也就是IWindowSession.Stub.Proxy类的成员函数relayout。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; IWindowSession.Stub.Proxy类的成员函数relayout获得了从WindowManagerService服务返回来的一个绘图表面，即一个Java层的Surface对象之后，就会将它的内容拷贝到参数outSurface所描述的另外一个Java层的Surface对象中，这里通过调用Surface类的成员函数readFromParcel来实现的。注意，参数outSurface所描述的这个Java层的Surface对象是在应用程序进程这一侧创建的，它的作用是用来绘制应用程序窗品的UI。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 接下来，我们就继续分析Surface类的成员函数readFromParcel的实现，以便可以了解在应用程序进程这一侧的Surface对象是如何创建的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; Step 10.&nbsp;Surface.readFromParcel</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_15_7144926"  code_snippet_id="129039" snippet_file_name="blog_20131226_15_7144926" name="code" class="java">public class Surface implements Parcelable {
    ......

    public native   void readFromParcel(Parcel source);
    ......

}</pre>&nbsp;&nbsp; &nbsp; &nbsp; 这个函数定义在文件frameworks/base/core/java/android/view/Surface.java中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;Surface类的成员函数readFromParcel是一个JNI方法，它是由C++层的函数Surface_readFromParcel来实现的，如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_16_7843038"  code_snippet_id="129039" snippet_file_name="blog_20131226_16_7843038" name="code" class="cpp">static void Surface_readFromParcel(
        JNIEnv* env, jobject clazz, jobject argParcel)
{
    Parcel* parcel = (Parcel*)env-&gt;GetIntField( argParcel, no.native_parcel);
    if (parcel == NULL) {
        doThrow(env, &quot;java/lang/NullPointerException&quot;, NULL);
        return;
    }

    sp&lt;Surface&gt; sur(Surface::readFromParcel(*parcel));
    setSurface(env, clazz, sur);
}</pre>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;这个函数定义在文件frameworks/base/core/jni/android_view_Surface.cpp中。<br /><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 参数argParcel所指向的一个Parcel对象的当前位置保存的是一个Java层的Surface对象的内容，函数readFromParcel首先调用C++层的Surface类的成员函数readFromParcel来将这些内容封装成一个C++层的Surface对象，如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_17_3001252"  code_snippet_id="129039" snippet_file_name="blog_20131226_17_3001252" name="code" class="cpp">sp&lt;Surface&gt; Surface::readFromParcel(const Parcel&amp; data) {
    Mutex::Autolock _l(sCachedSurfacesLock);
    sp&lt;IBinder&gt; binder(data.readStrongBinder());
    sp&lt;Surface&gt; surface = sCachedSurfaces.valueFor(binder).promote();
    if (surface == 0) {
       surface = new Surface(data, binder);
       sCachedSurfaces.add(binder, surface);
    }
    if (surface-&gt;mSurface == 0) {
      surface = 0;
    }
    cleanCachedSurfacesLocked();
    return surface;
}</pre>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;这个函数定义在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。<br /><p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;参数data所指向的一个Parcel对象的当前位置保存的是一个Binder代理对象，这个Binder代理对象实现了ISurface接口，它所引用的Binder本地对象就是在前面的Step 9中WindowManagerService服务请求SurfaceFlinger服务所创建的一个Layer对象的内部的一个SurfaceLayer对象。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;获得了一个实现了ISurface接口的Binder代理对象binder之后，C++层的Surface类的成员函数readFromParcel就可以将它封装在一个C++层的Surface对象中了，并且将这个C++层的Surface对象返回给调用者。注意，C++层的Surface类的成员函数readFromParcel在创建为Binder代理对象binder创建一个C++层的Surface对象之前，首先会在C++层的Surface类的静态成员变量sCachedSurfaces所描述的一个DefaultKeyedVectort向量中检查是否已经为存在一个对应的C++层的Surface对象了。如果已经存在，那么就会直接将这个C++层的Surface对象返回给调用者。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;回到函数Surface_readFromParcel中，接下来它就会调用另外一个函数setSurface来将前面所获得的一个C++层的Surface对象sur保存在参数clazz所描述的一个Java层的Surface对象的成员变量mNativeSurface中，如下所示：</p><p></p><pre code_snippet_id="129039" snippet_file_name="blog_20131226_18_8682853"  code_snippet_id="129039" snippet_file_name="blog_20131226_18_8682853" name="code" class="cpp">static void setSurface(JNIEnv* env, jobject clazz, const sp&lt;Surface&gt;&amp; surface)
{
    Surface* const p = (Surface*)env-&gt;GetIntField(clazz, so.surface);
    if (surface.get()) {
        surface-&gt;incStrong(clazz);
    }
    if (p) {
        p-&gt;decStrong(clazz);
    }
    env-&gt;SetIntField(clazz, so.surface, (int)surface.get());
}</pre>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;这个函数定义在文件frameworks/base/core/jni/android_view_Surface.cpp中。<p></p><p>&nbsp;&nbsp; &nbsp; &nbsp; 全局变量so是一个类型为so_t的结构体，在前面的Step 9中，我们已经分析过它的定义了，函数setSurface首先通过它的成员变量surface来将参数clazz所描述的一个Java层的Surface对象的成员变量mNativeSurface转换成一个Surface对象p。如果这个Surface对象p存在，那么就需要调用它的成员函数decStrong来减少它的强引用计数，因为接下来参数clazz所描述的一个Java层的Surface对象不再通过成员变量mNativeSurface来引用它了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;函数setSurface接下来就会将参数surface所指向的一个C++层的Surface对象的地址值保存在参数clazz所描述的一个Java层的Surface对象的成员变量mNativeSurface中。在执行这个操作之前，函数setSurface需要调用参数surface所指向的一个C++层的Surface对象的成员函数incStrong来增加它的强引用计数，这是因为接下来它要被参数clazz所描述的一个Java层的Surface对象通过成员变量mNativeSurface来引用了。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 至此，我们就分析完成Android应用程序窗口的绘图表面的创建过程了。通过这个过程我们就可以知道：</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 1. 每一个应用程序窗口都对应有两个Java层的Surface对象，其中一个是在WindowManagerService服务这一侧创建的，而另外一个是在应用程序进程这一侧创建的。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 2. 在WindowManagerService服务这一侧创建的Java层的Surface对象在C++层关联有一个SurfaceControl对象，用来设置应用窗口的属性，例如，大小和位置等。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 3.&nbsp;在应用程序进程这一侧创建的ava层的Surface对象在C++层关联有一个Surface对象，用来绘制应用程序窗品的UI。</p><p>&nbsp;&nbsp; &nbsp; &nbsp; 理解上述三个结论对理解Android应用程序窗口的实现框架以及WindowManagerService服务的实现都非常重要。&nbsp;一个应用程序窗口的绘图表面在创建完成之后，接下来应用程序进程就可以在上面绘制它的UI了。在接下来的一篇文章中，我们就继续分析Android应用程序窗品的绘制过程，敬请关注！</p><p style="text-align: center;"><span style="font-size:14px;color:#cc0000;"><strong>老罗的新浪微博：<a target=_blank target="_blank" href="http://weibo.com/shengyangluo">http://weibo.com/shengyangluo</a>，欢迎关注！</strong></span><br /></p>
</div>



<!-- Baidu Button BEGIN -->
<div id="bdshare" class="bdshare_t bds_tools get-codes-bdshare" style="float: right;">
<a class="bds_qzone"></a>
<a class="bds_tsina"></a>
<a class="bds_tqq"></a>
<a class="bds_renren"></a>
<a class="bds_t163"></a>
<span class="bds_more">更多</span>
<a class="shareCount"></a>
</div>
<!-- Baidu Button END -->


<!--192.168.100.34-->
<div class="article_next_prev">
            <li class="prev_article"><span>上一篇：</span><a href="/luoshengyang/article/details/8275938" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_shangyipian'])">Android应用程序窗口（Activity）与WindowManagerService服务的连接过程分析</a></li>
            <li class="next_article"><span>下一篇：</span><a href="/luoshengyang/article/details/8372924" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_xiayipian'])">Android应用程序窗口（Activity）的测量（Measure）、布局（Layout）和绘制（Draw）过程分析</a></li>
</div>

<!-- Baidu Button BEGIN -->
<script type="text/javascript" id="bdshare_js" data="type=tools&amp;uid=1536434" ></script>
<script type="text/javascript" id="bdshell_js"></script>
<script type="text/javascript">
    document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000)
</script>
<!-- Baidu Button END -->

        <div id="digg" ArticleId="8303098">
            <dl id="btnDigg" class="digg digg_disable">
                <dt onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_ding'])">顶</dt>
                <dd>13</dd>
            </dl>
            <dl id="btnBury" class="digg digg_disable">
                <dt onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_cai'])">踩</dt>
                <dd>0</dd>
            </dl>
        </div>
</div>
      <dl class="blog-associat-tag">
        <dt>相关主题推荐</dt>
        <dd>
                <a href="http://www.csdn.net/tag/android应用" target="_blank">android应用</a> 
                <a href="http://www.csdn.net/tag/全局变量" target="_blank">全局变量</a> 
                <a href="http://www.csdn.net/tag/新浪微博" target="_blank">新浪微博</a> 
                <a href="http://www.csdn.net/tag/interface" target="_blank">interface</a> 
                <a href="http://www.csdn.net/tag/exception" target="_blank">exception</a> 
        </dd>
    </dl> 
    <dl class="blog-ass-articl">
        <dt>相关博文推荐</dt>
            <dd>
                <a href="http://blog.csdn.net/xinrui625766/article/details/21185409" title="Unrecognized Windows Sockets error: 0: JVM_Bind 异常解决办法" target="_blank">Unrecognized Windows...</a></dd>
            <dd>
                <a href="http://blog.csdn.net/thelastperfect/article/details/21179155" title="java.lang.NoClassDefFoundError问题的解决办法" target="_blank">java.lang.NoClassDef...</a></dd>
            <dd>
                <a href="http://blog.csdn.net/qyqingyan/article/details/21173677" title="Android中共享全局变量" target="_blank">Android中共享全局变量</a></dd>
            <dd>
                <a href="http://blog.csdn.net/kingxie403/article/details/21170141" title="安卓访问webservice出现android.os.NetworkOnMainThreadException异常" target="_blank">安卓访问webservice出现andr...</a></dd>
            <dd>
                <a href="http://blog.csdn.net/aganlengzi/article/details/21167331" title="Static作用小结" target="_blank">Static作用小结</a></dd>
            <dd>
                <a href="http://blog.csdn.net/ttdevs/article/details/21156543" title="android网络开源框架volley(四)——谈谈图片加载续——九张图片相册的展示(微信微博等)" target="_blank">android网络开源框架volley(...</a></dd>
            <dd>
                <a href="http://blog.csdn.net/fzhmoive/article/details/21122809" title="try-catch-finally语句中有return语句时的执行情况" target="_blank">try-catch-finally语句中...</a></dd>
            <dd>
                <a href="http://blog.csdn.net/zslefour/article/details/21111851" title="VB.NET关于接口" target="_blank">VB.NET关于接口</a></dd>
    </dl>

    <div id="ad_cen">
        <script type="text/javascript">            BAIDU_CLB_SLOT_ID = "117306";</script>
        <script type="text/javascript" src="http://cbjs.baidu.com/js/o.js"></script>
    </div>
    <script type="text/javascript">
        //new Ad(4, 'ad_cen');
    </script>
<div id="comment_title" class="panel_head">
    查看评论<a name="comments"></a></div>
<div id="comment_list">
</div>
<div id="comment_bar">
</div>
<div id="comment_form">
</div>
<div class="announce">
    * 以上用户言论只代表其个人观点，不代表CSDN网站的观点或立场<a name="reply"></a><a name="quote"></a></div>
<script type="text/javascript">
    var fileName = '8303098';
    var commentscount = 17;
    var islock = false
</script>
<script type="text/javascript" src="http://static.blog.csdn.net/scripts/comment.js"></script>
    <div id="ad_bot">
    </div>
    <script type="text/javascript">
    new Ad(5, 'ad_bot');
    </script>
<div id="report_dialog">
</div>
<div id="d-top" style="display: none;">
    <a id="d-top-a" href="#" title="回到顶部" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_huidaodingbu'])">
        <img src="http://static.blog.csdn.net/images/top.png" alt="TOP" /></a>
</div>
<script type="text/javascript">
    $(function ()
    {
        var d_top = $('#d-top');
        document.onscroll = function ()
        {
            var scrTop = (document.body.scrollTop || document.documentElement.scrollTop);
            if (scrTop > 500)
            {
                d_top.show();
            } else
            {
                d_top.hide();
            }
        }
        $('#d-top-a').click(function ()
        {
            scrollTo(0, 0);
            this.blur();
            return false;
        });
    });
  
</script>
<style type="text/css">
    .tag_list
    {
        background: none repeat scroll 0 0 #FFFFFF;
        border: 1px solid #D7CBC1;
        color: #000000;
        font-size: 12px;
        line-height: 20px;
        list-style: none outside none;
        margin: 10px 2% 0 1%;
        padding: 1px;
    }
    .tag_list h5
    {
        background: none repeat scroll 0 0 #E0DBD3;
        color: #47381C;
        font-size: 12px;
        height: 24px;
        line-height: 24px;
        padding: 0 5px;
        margin: 0;
    }
    .tag_list h5 a
    {
        color: #47381C;
    }
    .classify
    {
        margin: 10px 0;
        padding: 4px 12px 8px;
    }
    .classify a
    {
        margin-right: 20px;
        white-space: nowrap;
    }
</style>
<div class="tag_list">
    <h5>
        <a href="http://www.csdn.net/tag/" target="_blank">核心技术类目</a></h5>
    <div class="classify">
<a title="全部主题" href="http://www.csdn.net/tag" target="_blank" onclick="LogClickCount(this,336);">全部主题</a>
<a title="Java" href="http://www.csdn.net/tag/Java" target="_blank" onclick="LogClickCount(this,336);">Java</a>
<a title="VPN" href="http://www.csdn.net/tag/vpn" target="_blank" onclick="LogClickCount(this,336);">VPN</a>
<a title="Android" href="http://www.csdn.net/tag/android" target="_blank" onclick="LogClickCount(this,336);">Android</a>
<a title="iOS" href="http://www.csdn.net/tag/ios" target="_blank" onclick="LogClickCount(this,336);">iOS</a>
<a title="ERP" href="http://www.csdn.net/tag/erp" target="_blank" onclick="LogClickCount(this,336);">ERP</a>
<a title="IE10" href="http://www.csdn.net/tag/ie10" target="_blank" onclick="LogClickCount(this,336);">IE10</a>
<a title="Eclipse" href="http://www.csdn.net/tag/eclipse" target="_blank" onclick="LogClickCount(this,336);">Eclipse</a>
<a title="CRM" href="http://www.csdn.net/tag/crm" target="_blank" onclick="LogClickCount(this,336);">CRM</a>
<a title="JavaScript" href="http://www.csdn.net/tag/javascript" target="_blank" onclick="LogClickCount(this,336);">JavaScript</a>
<a title="Ubuntu" href="http://www.csdn.net/tag/ubuntu" target="_blank" onclick="LogClickCount(this,336);">Ubuntu</a>
<a title="NFC" href="http://www.csdn.net/tag/nfc" target="_blank" onclick="LogClickCount(this,336);">NFC</a>
<a title="WAP" href="http://www.csdn.net/tag/wap" target="_blank" onclick="LogClickCount(this,336);">WAP</a>
<a title="jQuery" href="http://www.csdn.net/tag/jquery" target="_blank" onclick="LogClickCount(this,336);">jQuery</a>
<a title="数据库" href="http://www.csdn.net/tag/数据库" target="_blank" onclick="LogClickCount(this,336);">数据库</a>
<a title="BI" href="http://www.csdn.net/tag/bi" target="_blank" onclick="LogClickCount(this,336);">BI</a>
<a title="HTML5" href="http://www.csdn.net/tag/html5" target="_blank" onclick="LogClickCount(this,336);">HTML5</a>
<a title="Spring" href="http://www.csdn.net/tag/spring" target="_blank" onclick="LogClickCount(this,336);">Spring</a>
<a title="Apache" href="http://www.csdn.net/tag/apache" target="_blank" onclick="LogClickCount(this,336);">Apache</a>
<a title="Hadoop" href="http://www.csdn.net/tag/hadoop" target="_blank" onclick="LogClickCount(this,336);">Hadoop</a>
<a title=".NET" href="http://www.csdn.net/tag/.net" target="_blank" onclick="LogClickCount(this,336);">.NET</a>
<a title="API" href="http://www.csdn.net/tag/api" target="_blank" onclick="LogClickCount(this,336);">API</a>
<a title="HTML" href="http://www.csdn.net/tag/html" target="_blank" onclick="LogClickCount(this,336);">HTML</a>
<a title="SDK" href="http://www.csdn.net/tag/sdk" target="_blank" onclick="LogClickCount(this,336);">SDK</a>
<a title="IIS" href="http://www.csdn.net/tag/iis" target="_blank" onclick="LogClickCount(this,336);">IIS</a>
<a title="Fedora" href="http://www.csdn.net/tag/fedora" target="_blank" onclick="LogClickCount(this,336);">Fedora</a>
<a title="XML" href="http://www.csdn.net/tag/xml" target="_blank" onclick="LogClickCount(this,336);">XML</a>
<a title="LBS" href="http://www.csdn.net/tag/lbs" target="_blank" onclick="LogClickCount(this,336);">LBS</a>
<a title="Unity" href="http://www.csdn.net/tag/unity" target="_blank" onclick="LogClickCount(this,336);">Unity</a>
<a title="Splashtop" href="http://www.csdn.net/tag/splashtop" target="_blank" onclick="LogClickCount(this,336);">Splashtop</a>
<a title="UML" href="http://www.csdn.net/tag/uml" target="_blank" onclick="LogClickCount(this,336);">UML</a>
<a title="components" href="http://www.csdn.net/tag/components" target="_blank" onclick="LogClickCount(this,336);">components</a>
<a title="Windows Mobile" href="http://www.csdn.net/tag/windowsmobile" target="_blank" onclick="LogClickCount(this,336);">Windows Mobile</a>
<a title="Rails" href="http://www.csdn.net/tag/rails" target="_blank" onclick="LogClickCount(this,336);">Rails</a>
<a title="QEMU" href="http://www.csdn.net/tag/qemu" target="_blank" onclick="LogClickCount(this,336);">QEMU</a>
<a title="KDE" href="http://www.csdn.net/tag/kde" target="_blank" onclick="LogClickCount(this,336);">KDE</a>
<a title="Cassandra" href="http://www.csdn.net/tag/cassandra" target="_blank" onclick="LogClickCount(this,336);">Cassandra</a>
<a title="CloudStack" href="http://www.csdn.net/tag/cloudstack" target="_blank" onclick="LogClickCount(this,336);">CloudStack</a>
<a title="FTC" href="http://www.csdn.net/tag/ftc" target="_blank" onclick="LogClickCount(this,336);">FTC</a>
<a title="coremail" href="http://www.csdn.net/tag/coremail" target="_blank" onclick="LogClickCount(this,336);">coremail</a>
<a title="OPhone " href="http://www.csdn.net/tag/ophone " target="_blank" onclick="LogClickCount(this,336);">OPhone </a>
<a title="CouchBase" href="http://www.csdn.net/tag/couchbase" target="_blank" onclick="LogClickCount(this,336);">CouchBase</a>
<a title="云计算" href="http://www.csdn.net/tag/云计算" target="_blank" onclick="LogClickCount(this,336);">云计算</a>
<a title="iOS6" href="http://www.csdn.net/tag/iOS6" target="_blank" onclick="LogClickCount(this,336);">iOS6</a>
<a title="Rackspace " href="http://www.csdn.net/tag/rackspace " target="_blank" onclick="LogClickCount(this,336);">Rackspace </a>
<a title="Web App" href="http://www.csdn.net/tag/webapp" target="_blank" onclick="LogClickCount(this,336);">Web App</a>
<a title="SpringSide" href="http://www.csdn.net/tag/springside" target="_blank" onclick="LogClickCount(this,336);">SpringSide</a>
<a title="Maemo" href="http://www.csdn.net/tag/maemo" target="_blank" onclick="LogClickCount(this,336);">Maemo</a>
<a title="Compuware" href="http://www.csdn.net/tag/compuware" target="_blank" onclick="LogClickCount(this,336);">Compuware</a>
<a title="大数据" href="http://www.csdn.net/tag/大数据" target="_blank" onclick="LogClickCount(this,336);">大数据</a>
<a title="aptech" href="http://www.csdn.net/tag/aptech" target="_blank" onclick="LogClickCount(this,336);">aptech</a>
<a title="Perl" href="http://www.csdn.net/tag/perl" target="_blank" onclick="LogClickCount(this,336);">Perl</a>
<a title="Tornado" href="http://www.csdn.net/tag/tornado" target="_blank" onclick="LogClickCount(this,336);">Tornado</a>
<a title="Ruby" href="http://www.csdn.net/tag/ruby" target="_blank" onclick="LogClickCount(this,336);">Ruby</a>
<a title="Hibernate" href="http://www.csdn.net/hibernate" target="_blank" onclick="LogClickCount(this,336);">Hibernate</a>
<a title="ThinkPHP" href="http://www.csdn.net/tag/thinkphp" target="_blank" onclick="LogClickCount(this,336);">ThinkPHP</a>
<a title="Spark" href="http://www.csdn.net/tag/spark" target="_blank" onclick="LogClickCount(this,336);">Spark</a>
<a title="HBase" href="http://www.csdn.net/tag/hbase" target="_blank" onclick="LogClickCount(this,336);">HBase</a>
<a title="Pure" href="http://www.csdn.net/tag/pure" target="_blank" onclick="LogClickCount(this,336);">Pure</a>
<a title="Solr" href="http://www.csdn.net/tag/solr" target="_blank" onclick="LogClickCount(this,336);">Solr</a>
<a title="Angular" href="http://www.csdn.net/tag/angular" target="_blank" onclick="LogClickCount(this,336);">Angular</a>
<a title="Cloud Foundry" href="http://www.csdn.net/tag/cloudfoundry" target="_blank" onclick="LogClickCount(this,336);">Cloud Foundry</a>
<a title="Redis" href="http://www.csdn.net/tag/redis" target="_blank" onclick="LogClickCount(this,336);">Redis</a>
<a title="Scala" href="http://www.csdn.net/tag/scala" target="_blank" onclick="LogClickCount(this,336);">Scala</a>
<a title="Django" href="http://www.csdn.net/tag/django" target="_blank" onclick="LogClickCount(this,336);">Django</a>
<a title="Bootstrap" href="http://www.csdn.net/tag/bootstrap" target="_blank" onclick="LogClickCount(this,336);">Bootstrap</a>
    </div>
</div>

                    <div class="clear">
                    </div>
                </div>
            </div>
            <div id="side">
    <div class="side">
<div id="panel_Profile" class="panel">
<ul class="panel_head"><span>个人资料</span></ul>
<ul class="panel_body profile">
<div id="blog_userface">
    <a href="http://my.csdn.net/Luoshengyang" target="_blank">
    <img src="http://avatar.csdn.net/5/6/E/1_luoshengyang.jpg" title="访问我的空间" style="max-width:90%"/>
    </a>
    <br />
    <span><a href="http://my.csdn.net/Luoshengyang" class="user_name" target="_blank">Luoshengyang</a></span>
</div>
<div class="interact">
<a href="javascript:void(0);" class="attent" id="span_add_follow" title="[加关注]" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_guanzhu'])"></a>
<a href="javascript:void(0);" class="letter" onclick="loginto(1)" title="[发私信]" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_sixin'])"></a>
</div>
<div id="blog_medal">
        <div class="ico_expert" title="CSDN认证专家"></div>
</div>
<ul id="blog_rank">
    <li>访问：<span>3165265次</span></li>
    <li>积分：<span>16463分</span></li>
    <li>排名：<span>第128名</span></li>
</ul>
<ul id="blog_statistics">
    <li>原创：<span>100篇</span></li>
    <li>转载：<span>0篇</span></li>
    <li>译文：<span>0篇</span></li>
    <li>评论：<span>5216条</span></li>
</ul>
</ul>
</div>


<div id="custom_column_6567056" class="panel">
<ul class="panel_head"><span>博客公告</span></ul>
<ul class="panel_body">
<b><font color="red" size="2">本博客所有文章均为原创，欢迎交流，欢迎转载；转载请勿篡改内容，并且注明出处，禁止用于商业目的，谢谢！</font></b>
</ul>
</div><div id="custom_column_21664874" class="panel">
<ul class="panel_head"><span>图书信息</span></ul>
<ul class="panel_body">
<font color="red"><b>书名：</b></font>
<p>《Android系统源代码情景分析》</p>

<p><a href="http://wenku.it168.com/d_000574719.shtml" target="_blank">试读请点击</a></p>

<p><font color="red"><b>出版社：</b></font></p>
<p>电子工业出版社</p>

<p><font color="red"><b>网店：</b></font></p>

<p>1. <a href="http://product.dangdang.com/product.aspx?product_id=22884086" target="_blank">当当网（满100减20，满200减50）</a></p><p></p>

<p>2. <a href="http://book.360buy.com/11104739.html" target="_blank">京东网（满99减20）</a></p><p></p>

<p>3. <a href="http://www.amazon.cn/Android%E7%B3%BB%E7%BB%9F%E6%BA%90%E4%BB%A3%E7%A0%81%E6%83%85%E6%99%AF%E5%88%86%E6%9E%90-%E7%BD%97%E5%8D%87%E9%98%B3/dp/B009OLU8EE" target="_blank">亚马逊网（满100减20，满200减50）</a></p><p></p>

<p>4. <a href="http://product.china-pub.com/3684413" target="_blank">互动出版网</a></p><p></p>

<p>5. <a href="http://www.suning.com/emall/prd_10052_22001_-7_3435777_.html" target="_blank">苏宁易购网</a></p><p></p>

<p><a href="http://s.etao.com/search?spm=1002.8.1.1386.g76Rk3&amp;q=Android%CF%B5%CD%B3%D4%B4%B4%FA%C2%EB%C7%E9%BE%B0%B7%D6%CE%F6&amp;style=list&amp;initiative_id=etao_20121011&amp;pn=1&amp;sort=sale-desc" target="_blank">比一比谁更实惠</a></p>

<p><font color="red"><b>号外：</b></font></p>

<p>本书繁体版已经成功输出到台湾</p>

<p></p>
</ul>
</div><div id="custom_column_21603386" class="panel">
<ul class="panel_head"><span>联系方式</span></ul>
<ul class="panel_body">
<font color="red"><b>新浪微博：</b></font>
<p><a href="http://weibo.com/shengyangluo" target="_blank"><font color="red"><b>http://weibo.com/shengyangluo</b></font></a></p><p></p>

<font color="red"><b>QQ交流群：</b></font>
<p><b><font color="red">130112760</font></b>（1000人群，已满）</p>
<p><b><font color="red">248749286</font></b>（1000人群，已满）</p>
<p><b><font color="red">204155322</font></b>（500人群，未满）</p>
<p><b><font color="red">278558417</font></b>（500人群，已满）</p>
<p><b><font color="red">303165655</font></b>（500人群，已满）</p>
<p>PS：请勿同时加入多个群，一经发现，永久封号，谢谢！</p>
</ul>
</div><div id="panel_Category" class="panel">
    <ul class="panel_head"><span>博客专栏</span></ul>
    <ul class="panel_body" id="sp_column">
    <table cellpadding="0" cellspacing="0"><tr>
    <td style="padding:10px 10px 0 0;">
    <a href="http://blog.csdn.net/column/details/androidluo.html" target="_blank"><img src="http://avatar.csdn.net/blogpic/20111107095438958.jpg" style="width:75px;height:75px;" /></a>
    </td>
    <td style="padding:10px 0; vertical-align:top;">
    <a href="http://blog.csdn.net/column/details/androidluo.html" target="_blank">老罗的Android之旅</a>
    <p>文章：100篇</p>
    <span>阅读：3169106</span>
    </td>
    </tr></table>
    </ul>
</div><div id="hotarticls" class="panel">
<ul class="panel_head"><span>阅读排行</span></ul>
<ul class="panel_body itemlist">
<li>
<a href="/luoshengyang/article/details/6559955" title="在Ubuntu上下载、编译和安装Android最新源代码">在Ubuntu上下载、编译和安装Android最新源代码</a><span>(109478)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6618363" title="Android进程间通信（IPC）机制Binder简要介绍和学习计划">Android进程间通信（IPC）机制Binder简要介绍和学习计划</a><span>(95385)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6689748" title="Android应用程序启动过程源代码分析">Android应用程序启动过程源代码分析</a><span>(80910)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6568411" title="在Ubuntu上为Android系统编写Linux内核驱动程序">在Ubuntu上为Android系统编写Linux内核驱动程序</a><span>(78655)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8923485" title="那两年炼就的Android内功修养">那两年炼就的Android内功修养</a><span>(76761)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6567257" title="Android硬件抽象层（HAL）概要介绍和学习计划">Android硬件抽象层（HAL）概要介绍和学习计划</a><span>(72556)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6564592" title="在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）">在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）</a><span>(66727)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6557518" title="Android学习启动篇">Android学习启动篇</a><span>(59079)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6566662" title="如何单独编译Android源代码中的模块">如何单独编译Android源代码中的模块</a><span>(58027)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6575988" title="在Ubuntu为Android硬件抽象层（HAL）模块编写JNI方法提供Java访问硬件服务接口">在Ubuntu为Android硬件抽象层（HAL）模块编写JNI方法提供Java访问硬件服务接口</a><span>(56751)</span>
</li>
</ul>
</div>
<div id="hotarticls2" class="panel">
<ul class="panel_head"><span>评论排行</span></ul>
<ul class="panel_body itemlist">
<li>
<a href="/luoshengyang/article/details/6689748" title="Android应用程序启动过程源代码分析">Android应用程序启动过程源代码分析</a><span>(322)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6559955" title="在Ubuntu上下载、编译和安装Android最新源代码">在Ubuntu上下载、编译和安装Android最新源代码</a><span>(238)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8116866" title="《Android系统源代码情景分析》一书勘误">《Android系统源代码情景分析》一书勘误</a><span>(208)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6882903" title="Android应用程序键盘（Keyboard）消息处理机制分析">Android应用程序键盘（Keyboard）消息处理机制分析</a><span>(182)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8923485" title="那两年炼就的Android内功修养">那两年炼就的Android内功修养</a><span>(182)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6580267" title="在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务">在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务</a><span>(170)</span>
</li>
<li>
<a href="/luoshengyang/article/details/7409491" title="Android博客文章整理">Android博客文章整理</a><span>(167)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6568411" title="在Ubuntu上为Android系统编写Linux内核驱动程序">在Ubuntu上为Android系统编写Linux内核驱动程序</a><span>(151)</span>
</li>
<li>
<a href="/luoshengyang/article/details/8452527" title="2012年的Android之旅：梦想、学习、坚持、自信、淡定">2012年的Android之旅：梦想、学习、坚持、自信、淡定</a><span>(139)</span>
</li>
<li>
<a href="/luoshengyang/article/details/6564592" title="在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）">在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）</a><span>(136)</span>
</li>
</ul>
</div>
<div id="newcomments" class="panel">
<ul class="panel_head"><span>最新评论</span></ul>
<ul class="panel_body itemlist">
    <li>
    <a href="/Luoshengyang/article/details/8923485#comments">那两年炼就的Android内功修养</a>
    <p style="margin:0px;"><a href="/h137286510" class="user_name">h137286510</a>:
我是搞底层显示驱动的，最近想研究下surface，就百度下跑你这来了。看完你写的surface系列，...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6786239#comments">Android系统的智能指针（轻量级指针、强指针和弱指针）的实现原理分析</a>
    <p style="margin:0px;"><a href="/ssikyou" class="user_name">ssikyou</a>:
顶老罗！补充，多调用的addWeakRef在4.4里已经没有了，OBJECT_LIFETIME_FO...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6817933#comments">Android应用程序消息处理机制（Looper、Handler）分析</a>
    <p style="margin:0px;"><a href="/yuanshisenlin123" class="user_name">yuanshisenlin123</a>:
做了将近两年的Android应用开发，表示没看懂，看来还得多多学习哇！
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/19023609#comments">Android源代码编译命令m/mm/mmm/make分析</a>
    <p style="margin:0px;"><a href="/thinkinwm" class="user_name">thinkinwm</a>:
好文章！
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6568411#comments">在Ubuntu上为Android系统编写Linux内核驱动程序</a>
    <p style="margin:0px;"><a href="/HuiyuYang_fish" class="user_name">HuiyuYang_fish</a>:
期待实验~
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6557518#comments">Android学习启动篇</a>
    <p style="margin:0px;"><a href="/HuiyuYang_fish" class="user_name">HuiyuYang_fish</a>:
貌似我也要开始android的初步涉猎了~
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/12957169#comments">《老罗的Android之旅》导读PPT</a>
    <p style="margin:0px;"><a href="/mianju23" class="user_name">mianju23</a>:
牛人
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/8452527#comments">2012年的Android之旅：梦想、学习、坚持、自信、淡定</a>
    <p style="margin:0px;"><a href="/wuxiaoming1733" class="user_name">wuxiaoming1733</a>:
顶楼主
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6737352#comments">Android应用程序注册广播接收器（registerReceiver）的过程分析</a>
    <p style="margin:0px;"><a href="/wuxiaoming1733" class="user_name">wuxiaoming1733</a>:
罗大神你好，我这边还没明白，怎么自己用代码，实现androidmanifest.xml里静态注册系统...
    </p>
    </li>
    <li>
    <a href="/Luoshengyang/article/details/6642463#comments">Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析</a>
    <p style="margin:0px;"><a href="/zqz19891031" class="user_name">zqz19891031</a>:
博主您好，看了你的博客，收获颇多！有一个问题想请教：最近在看Android的Binder实现，看到B...
    </p>
    </li>
</ul>
</div>
    </div>
    <div class="clear">
    </div>
</div>

            <div class="clear">
            </div>
        </div>
        

<script type="text/javascript" src="http://static.blog.csdn.net/scripts/newblog.min.js"></script>
<script type="text/javascript" src="http://medal.blog.csdn.net/showblogmedal.ashx?blogid=1134701"></script>
<script type="text/javascript">
    document.write('<script type="text/javascript" src="http://csdnimg.cn/pubfooter/js/publib_footer.js?' + Math.floor(new Date() / 120000).toString(36) + '="></' + 'script>');
</script>
    <script type="text/javascript" src="http://passport.csdn.net/content/loginbox/login.js"></script>
<script type="text/javascript">document.write("<img src=http://counter.csdn.net/pv.aspx?id=24 border=0 width=0 height=0>");</script>
<script type="text/javascript" src="http://www.csdn.net/ui/scripts/Csdn/counter.js"></script>
<script type="text/javascript" src="http://ad.csdn.net/scripts/ad-blog.js"></script>
<script type="text/javascript" src="http://zz.csdn.net/js/count.js"></script>
<script type="text/javascript">
    $(function () {
        function __get_code_toolbar(snippet_id) {
            return $("<a href='https://code.csdn.net/snippets/"
                    + snippet_id
                    + "' target='_blank' title='在CODE上查看代码片' style='text-indent:0;'><img src='https://code.csdn.net/assets/CODE_ico.png' width=12 height=12 alt='在CODE上查看代码片' style='position:relative;top:1px;left:2px;'/></a>"
                    + "<a href='https://code.csdn.net/snippets/"
                    + snippet_id
                    + "/fork' target='_blank' title='派生到我的代码片'  style='text-indent:0;'><img src='https://code.csdn.net/assets/ico_fork.svg' width=12 height=12 alt='派生到我的代码片' style='position:relative;top:2px;left:2px;'/></a>");
        }
        
        $("[code_snippet_id]").each(function () {
            __s_id = $(this).attr("code_snippet_id");
            if (__s_id != null && __s_id != "" && __s_id != 0 && parseInt(__s_id) > 70020) {
                __code_tool = __get_code_toolbar(__s_id);
                $(this).prev().find(".tools").append(__code_tool);
            }
        });
    });
</script>

    </div>
      <!--new top-->
    
    <script id="csdn-toolbar-id" btnId="header_notice_num" wrapId="note1" count="5" subCount="5" type="text/javascript" src="http://static.csdn.net/public/common/toolbar/js/toolbar.js"></script>     <!--new top-->
</body>
</html>
